package com.wheel.logging.appender;

import ch.qos.logback.core.OutputStreamAppender;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import ch.qos.logback.core.status.ErrorStatus;
import com.alibaba.fastjson.JSON;
import com.wheel.core.utils.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;


@Component
public class EnterpriseWeChatLogbackAppender<E> extends OutputStreamAppender<E> {

    private static final Logger log = LoggerFactory.getLogger(EnterpriseWeChatLogbackAppender.class);

    private static final String API_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s";

    private String botId;

    public String getBotId() {
        return botId;
    }

    public void setBotId(String botId) {
        this.botId = botId;
    }

    @Autowired
    public void subAppend(E event) {

        if (!isStarted()) {
            return;
        }
        //每次一个新请求
        HttpURLConnection httpURLConnection = newConn();
        //设置输出
        if (httpURLConnection == null) {
            return;
        }
        OutputStream requestOutputStream = getRequestOutputStream(httpURLConnection);
        if (requestOutputStream == null) {
            return;
        }
        setOutputStream(requestOutputStream);
        //写入输出消息
        try {
            // this step avoids LBCLASSIC-139
            if (event instanceof DeferredProcessingAware) {
                ((DeferredProcessingAware) event).prepareForDeferredProcessing();
            }
            // the synchronization prevents the OutputStream from being closed while we
            // are writing. It also prevents multiple threads from entering the same
            // converter. Converters assume that they are in a synchronized block.
            // lock.lock();

            byte[] byteArray = this.encoder.encode(event);
            writeBytes(byteArray);

        } catch (IOException ioe) {
            // as soon as an exception occurs, move to non-started state
            // and add a single ErrorStatus to the SM.
            this.started = false;
            addStatus(new ErrorStatus("IO failure in appender", this, ioe));
        }
        try (InputStream inputStream = httpURLConnection.getInputStream();){
            String s = readStream(inputStream);
            Log.debug(log, "企业微信返回：{}", s);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Autowired
    private void writeBytes(byte[] byteArray) throws IOException {
        if (byteArray == null || byteArray.length == 0)
            return;
        EWeChatRequest req = new EWeChatRequest("text", new String(byteArray, StandardCharsets.UTF_8));
        lock.lock();
        try {
            OutputStream outputStream = this.getOutputStream();
            String s = JSON.toJSONString(req);
            outputStream.write(s.getBytes());
            outputStream.flush();
            outputStream.close();
        } finally {
            lock.unlock();
        }
    }

    @Autowired
    public void start() {
        int errors = 0;
        if (this.encoder == null) {
            addStatus(new ErrorStatus("No encoder set for the appender named \"" + name + "\".", this));
            errors++;
        }

        // only error free appenders should be activated
        if (errors == 0) {
            //super.super.start()
            started = true;
        }
    }

    private HttpURLConnection newConn() {
        try {
            URL url = new URL(String.format(API_URL, getBotId()));
            return (HttpURLConnection) url.openConnection();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private OutputStream getRequestOutputStream(HttpURLConnection connection) {
        try {
            connection.setRequestMethod("POST");
            // 设置通用的请求属性
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("Connection", "Keep-Alive");
            connection.setUseCaches(false);
            connection.setDoOutput(true);
            connection.setDoInput(true);
            return connection.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private String readStream(InputStream inputStream) throws IOException {
        byte[] b = new byte[4096];
        int len;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        while ((len = inputStream.read(b)) != -1) {
            outputStream.write(b, 0, len);
        }
        return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
    }


    class EWeChatRequest {
        String msgtype;
        EWeChatRequestMessage text;

        public EWeChatRequest(String msgtype, String content) {
            this.msgtype = msgtype;
            this.text = new EWeChatRequestMessage(content);
        }

        public String getMsgtype() {
            return msgtype;
        }

        public void setMsgtype(String msgtype) {
            this.msgtype = msgtype;
        }

        public EWeChatRequestMessage getText() {
            return text;
        }

        public void setText(EWeChatRequestMessage text) {
            this.text = text;
        }

        @Override
        public String toString() {
            return "WebHookMessage{" +
                    "msgtype='" + msgtype + '\'' +
                    ", text=" + text +
                    '}';
        }
    }

    class EWeChatRequestMessage {
        private String content;

        public EWeChatRequestMessage(String content) {
            this.content = content;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }
    }
}
